﻿/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * A.I. Protected Data File Class
 *      Provides an AI file format implementation for reading and writing 
 * secure (encrypted) data files using RSA to protect the symmetric key data.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using Adaptive.Foundation.Security;

namespace Adaptive.Foundation.IO
{
    /// <summary>
    /// Provides an AI file format implementation for reading and writing 
    /// secure (encrypted) data files using RSA to protect the symmetric key data.
    /// </summary>
    public sealed class AiProtectedFile : AiSecureFile  
    {
        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// RSA cryptographic utility.
        /// </summary>
        private RsaCryptoDevice _device;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class to the specified file name.
        /// </summary>
        /// <param name="fileName">
        /// The fully-qualified path and name of the file to be read or written.
        /// </param>
        public AiProtectedFile(string fileName)
            : base(fileName)
        {
            _device = new RsaCryptoDevice();
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                if (_device != null)
                    _device.Dispose();
            }
            _device = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets or sets the RSA public key value for the file.
        /// </summary>
        /// <value>
        /// A byte array containing an RSA public encryption/decryption key.
        /// </value>
        public byte[] PublicKey
        {
            get { return _device.Key; }
            set { _device.Key = value; }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods / Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods / Event Methods
        /// <summary>
        /// Reads the extra header data and raises the <see cref="AiDataFileBase.PostHeaderRead"/> event.
        /// </summary>
        /// <param name="e">
        /// A <see cref="FileStreamEventArgs"/> event arguments instance containing a reference
        /// to the file stream being used.
        /// </param>
        protected override void OnPostHeaderRead(FileStreamEventArgs e)
        {
            BinaryReader reader = new BinaryReader(e.Stream);

            _device.Key = reader.ReadBytes(128);
            base.Key = DecryptData(reader.ReadBytes(128));
            base.IV = DecryptData(reader.ReadBytes(128));
            HeaderAdjustment = (128 * 3);
            reader = null;

            base.OnPostHeaderRead(e);
        }
        /// <summary>
        /// Writes the extra header data and raises the <see cref="AiDataFileBase.PostHeaderWrite"/> event.
        /// </summary>
        /// <param name="e">
        /// A <see cref="FileStreamEventArgs"/> event arguments instance containing a reference
        /// to the file stream being used.
        /// </param>
        protected override void OnPostHeaderWrite(FileStreamEventArgs e)
        {            
            BinaryWriter writer = new BinaryWriter(e.Stream);
            
            writer.Write(_device.Key);//128
            writer.Write(EncryptData(base.Key));
            writer.Write(EncryptData(base.IV));
            writer.Flush();
            writer = null;

            base.OnPostHeaderWrite(e);

        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions

        private byte[] EncryptData(byte[] originalData)
        {
            CryptographicOperation operation = null;
            byte[] returnValue = null;

            operation = new CryptographicOperation(CryptographicAlgorithm.Rsa, CryptographicOperationType.Encrypt);
            operation.SourceData = new MemoryStream(originalData);
            _device.PerformOperation(operation);
            if (operation.Result.ResultType == CryptographicResultType.Success)
            {
                BinaryReader reader = new BinaryReader( operation.Result.ResultData );
                operation.Result.ResultData.Seek(0, SeekOrigin.Begin);
                returnValue = reader.ReadBytes((int)operation.Result.ResultData.Length);
                reader = null;
            }
            operation.Dispose();
            operation = null;

            return returnValue;
        }
        private byte[] DecryptData(byte[] originalData)
        {
            CryptographicOperation operation = null;
            byte[] returnValue = null;

            operation = new CryptographicOperation(CryptographicAlgorithm.Rsa, CryptographicOperationType.Decrypt);
            operation.SourceData = new MemoryStream(originalData);
            _device.PerformOperation(operation);
            if (operation.Result.ResultType == CryptographicResultType.Success)
            {
                BinaryReader reader = new BinaryReader(operation.Result.ResultData);
                operation.Result.ResultData.Seek(0, SeekOrigin.Begin);
                returnValue = reader.ReadBytes((int)operation.Result.ResultData.Length);
                reader = null;
            }
            operation.Dispose();
            operation = null;

            return returnValue;
        }
        #endregion

    }
}